﻿package net.molchat.android.joint;


import java.io.IOException;
import java.io.InterruptedIOException;

import net.molchat.android.cont.Cont;
import net.molchat.android.cont.ContOne;
import net.molchat.android.cont.ContRoom;
import net.molchat.android.core.Man;
import net.molchat.android.core.Mat;
import net.molchat.android.core.Mess;
import net.molchat.android.core.Setting;
import net.molchat.android.core.Tick;
import net.molchat.android.core.TickEvent;
import net.molchat.android.media.MediaType;
import net.molchat.android.mess.MessOne;
import net.molchat.android.mess.MessRoom;
import net.molchat.android.scrn.theme.ContCol;
import android.util.Log;


// FIXME Commented out class


// ======================================================================= IOMan
/**
 * Input/output manager. Holds network connections, commands transmitting and
 * processing.
 * 
 * @author Valentyn Markovych
 * 
 */
public final class IOMan extends Thread implements TickEvent {


//==================================================================== Constants
private final int BLK_SIZE = 512; // Размер считываемого текстового фрагмента
public final int P_HIST = 50; // Пауза при приеме истории
public final int P_WORK = 1000; // Пауза в рабочем режиме


//======================================================================= Fields
private IO io; // Input/output object
public String status = ""; // Status line
private Tick kaTick; // Таймер вызова функции проверки наличия связи
private final int kaPeriod = (4 * 60 + 50) * 1000; // Период проверки наличия связи, мс
private boolean fPause = true; // Флаг паузы
public boolean fOnline = false; // В сети
private boolean fRun = true; // Флаг времени жизни
public int timeSleep = P_HIST; // Текущее значение паузы


////=================================================================== Константы
//
//
////Соединение
//private IO io; // Объект ввода/вывода
//
//
////Другое
//public String status = ""; // Статусная строка
//
//


//================================================================== Constructor
public IOMan() {

	Log.v("IOMan.init", "=====");
	this.start();
}


//========================================================================= tick
/**
 * Tick event processing.
 * 
 * @return
 */
public boolean tick() {

	keepAlive();
	return false;
}


/**
 * Send keepAlive message to hold the connection always open.
 */
private void keepAlive() {

	setStatus("Сеть временно недоступна");
	try {
		outKeepAlive();
	} catch (Exception e) {
		Log.e("IOMan.tick", "Не могу отправить сигнал `на связи`", e);
	}
}


//======================================================================== pause
public void pause() {

	synchronized (this) {
		fPause = true;
	}
}


//====================================================================== connect
/**
 * Disconnect any old connection and create new one.
 * 
 */
public void connect() {

	disconnect(Mess.LOCAL, "Preparing for new connection");
	io = new IO();
}


//=================================================================== disconnect
/**
 * Disconect existing connection.
 * 
 * @param from
 *          Direction
 * @param why
 *          Reason text
 */
public void disconnect(int from, String why) {

	Log.v("IOMan.disconnect", "Disconnecting: " + why + " from =" + from);
	try {
		pause();

		if (kaTick != null) {
			kaTick.exit();
			kaTick = null;
		}

		if (io != null) {
			if (from >= Mess.LOCAL) {
				io.close();
			}
			io = null;
		}

		if (from <= Mess.LOCAL) {
			setStatus("Не в сети. " + why);
			setOnline(false);
		}
	} catch (Exception e) {
		Log.e("IOMan.disconnect",
				"Disconnection error while disconnecting because: " + why, e);
	}
}


//================================================================ connConnected
/**
 * Callback for change network state to connected.
 */
public void connConnected() {

	Log.v("IOMan.connConnected", "Соединен");

	try {
		setStatus("Соединен");

		kaTick = new Tick(kaPeriod, this);
		kaTick.resumeTick();

		outLogin();

		//		if (Setting.camImgEnc == null) {
		//			byte[] setBytes = Setting.toBytes(new int[] { Setting.CAM_IMG_ABIL,
		//					Setting.DISP_W, Setting.DISP_H });
		//			outSetting(setBytes);
		//		}

	} catch (Exception e) {
		Log.e("IOMan.connConnected", "unexpected", e);
		disconnect(Mess.LOCAL, "Подключиться не удалось");
	}
}


//============================================================= connNotConnected
/**
 * Callback for change network state to DISconnected.
 */
public void connNotConnected(String errorText, Exception exception) {

	Log.v("IOMan.connNotConnected", errorText, exception);
	disconnect(Mess.LOCAL, errorText);
}


//========================================================================== run
/**
 * Receiving dispatcher.
 * 
 */
public void run() {

	Log.v("IOMan.run", "Старт приема сообщений");
	int messType = 0; // Тип сообщения

	while (fRun) {
		try {
			synchronized (this) {
				while (fRun && fPause) {
					this.wait(); // Произвольный интервал ожидания для исключения полного зависания потока
				}
			}

			// Цикл чтения сообщения
			kaTick.update();
			sleep(timeSleep); // Страховка от перегрузки спамом

			// Читаем заголовок
			messType = io.readInt();

			Log.v("IOMan.run", "> Входящее. Тип " + messType);

			// ------------------------------------- Обработка типов
			switch (messType) {
				case ComType.mKeepAlive: // Сигнал поддержания связи
					setStatus("В сети");
					break;
				case ComType.mKeepAliveTime:
					Log.v("IOMan.run", "> Входящее. Тип " + messType);
					//					inKeepAliveTime();
					break;
				case ComType.vContList: // Список контактов
					inContList();
					break;
				case ComType.vResOne: // Подтверждение доставки
					Log.v("IOMan.run", "> Входящее. Тип " + messType);
					//					inResOne();
					break;
				case ComType.vResRoom: // Подтверждение доставки в комнату
					Log.v("IOMan.run", "> Входящее. Тип " + messType);
					//					inResRoom();
					break;
				case ComType.vServ:
					Log.v("IOMan.run", "> Входящее. Тип " + messType);
					//					inServ();
					break;
				case ComType.vStatOne: // Статус контакта
					Log.v("IOMan.run", "> Входящее. Тип " + messType);
					//					inStatOne();
					break;
				case ComType.vStatRoom: // Статус комнаты
					Log.v("IOMan.run", "> Входящее. Тип " + messType);
					//					inStatRoom();
					break;
				case ComType.vTextOne:
					inTextOne();
					break;
				case ComType.vTextRoom:
					inTextRoom();
					break;
				case ComType.vDelCont: // Контакт удален !c 2010.01.12!
					Log.v("IOMan.run", "> Входящее. Тип " + messType);
					//					inDelCont();
					break;
				case ComType.vSetting:
					Log.v("IOMan.run", "> Входящее. Тип " + messType);
					//					inSetting();
					break;
				case ComType.mBye: // Завершить сеанс
				case -1: // Чтение прервано пользователем
					disconnect(Mess.LOCAL, "Связь потеряна");
					break;
				default:
					Log.e("IOMan.run", "> Неизвестный тип сообщения: " + messType);
					io.readBytes(io.readInt()); // Читаем длину и содержимое без обработки
					break;
			} // Конец обработки команд
			//			Log.v("IOMan.run", "Отработано. Тип " + messType);

		} catch (InterruptedIOException e) {
			Log.v("IOMan.run", "Связь прервана");
		} catch (IOException e) {
			disconnect(Mess.LOCAL, "Связь потеряна");
			Log.e("IOMan.run", "unexpected", e);
		} catch (InterruptedException e) {
			Log.v("IOMan.run", "Прерваны");
		} catch (Exception e) {
			Log.e("IOMan.run", "Ошибка приема", e);
			disconnect(Mess.LOCAL, "Ошибка приема");
		}
	} // Конец основоного цикла приема
	/**/
	Log.v("IOMan.run", "Exit thread");
}


//===================================================================== outLogin
/**
 * Send login credentials.
 * 
 */
private void outLogin() {

	Log.v("IOMan.outLogin", "< Login credintials sending");

	try {
		//		InputStream is = this.getClass().getResourceAsStream("/res/empty.gif");
		byte[] b = new byte[16];
		//		is.read(b);
		//		is.close();

		io.start(Setting.TYPE);
		// Версия
		io.sendBytes(new byte[] {
				(byte) ((Integer.parseInt(Setting.VER.substring(0, 2))) & 0xff),
				(byte) ((Integer.parseInt(Setting.VER.substring(3, 5))) & 0xff),
				(byte) ((Integer.parseInt(Setting.VER.substring(6, 10)) - 2000) & 0xff) });


		// Идентификатор
		io.sendInt(ComType.mID);
		io.sendBytes(b);

		// Логин и пароль
		io.sendInt(ComType.vLogin);
		io.sendString(Setting.login);
		io.sendString(Setting.password);

		// Контрольные суммы CRC
		int crc = -1;

		for (int q = Cont.ROOM; q <= Cont.ONE; ++q) {
			crc = Man.contMan.getCRC(q);
			io.sendInt(ComType.vCRC);
			io.sendInt(q);
			io.sendInt(crc);
		}

		io.flush();

		kaTick.update();

		setOnline(true);
		resumeThr();

	} catch (Exception e) {
		Log.e("IOMan.outLogin", "Ошибка передачи идентификаторов", e);
	}

}


//==================================================================== setStatus
/**
 * Set status line.
 * 
 * @param statusText
 *          Text to show.
 */
private void setStatus(String statusText) {

	status = statusText;
	Man.scrMan.chStat(statusText);
}


//=============================================================== Возобновление
public void resumeThr() {

	Log.v("IOMan.resume", "Продолжение работы менеджера");
	synchronized (this) {
		if (io != null) {
			fPause = false;
			notifyAll();
		}
	}
}


//==================================================================== setOnline
private void setOnline(boolean fOnline) {

	this.fOnline = fOnline;

	Man.contMan.fOnline(fOnline);
	Man.scrMan.fOnline(fOnline);
}


//// ==================================================================== Соединен
//public void connConnected() {
//
//	Log.v("IOMan.connConnected", "Соединен");
//
//	try {
//		setStatus("Соединен");
//
//		kaTick = new Tick(kaPeriod, this);
//		kaTick.resumeTick();
//
//		outLogin();
//
//		if (Setting.camImgEnc == null) {
//			byte[] setBytes = Setting.toBytes(new int[] { Setting.CAM_IMG_ABIL,
//					Setting.DISP_W, Setting.DISP_H });
//			outSetting(setBytes);
//		}
//
//	} catch (Exception e) {
//		Log.e("IOMan.connConnected", e);
//		disconnect(Mess.LOCAL, "Подключиться не удалось");
//	}
//}
//
//
//// ====================================================== Соединиться не удалось
//public void connNotConnected(String errorText, Exception exception) {
//
//	Log.v("IOMan.connNotConnected", errorText, exception);
//	disconnect(Mess.LOCAL, errorText);
//}
//
//
//// =============================================================== Разъединиться
//public void disconnect(int from, String why) {
//
//	Log.v("IOMan.disconnect", "Разъединяюсь: " + why + " from =" + from);
//	try {
//		pause();
//
//		if (kaTick != null) {
//			kaTick.exit();
//			kaTick = null;
//		}
//
//		if (io != null) {
//			if (from >= Mess.LOCAL) {
//				io.close();
//			}
//			io = null;
//		}
//
//		if (from <= Mess.LOCAL) {
//			setStatus("Не в сети. " + why);
//			setOnline(false);
//		}
//	} catch (Exception e) {
//		Log.e("IOMan.disconnect", "Ошибка при разъединении по причине " + why, e);
//	}
//}
//
//
//// ======================================================================= Выход
//public void exit(boolean fOk, int from) {
//
//	if (fOk && (io != null)) {
//		try {
//			io.start(ComType.mBye);
//			io.flush();
//		} catch (Exception e) {
//		}
//	}
//
//	fRun = false;
//	fPause = false;
//
//	disconnect(from, "Работа программы завершена");
//	synchronized (this) {
//		notifyAll();
//	}
//	io = null;
//	kaTick = null;
//
//}


//=================================================================== inContList
private void inContList() throws IOException {

	int len = io.readInt(); // Общая длина
	io.startRead();
	int contType = io.readInt(); // Тип списка контактов
	int add = io.readInt(); // Постоянство или номер комнаты
	int num = io.readInt(); // Кол-во полученных результатов

	Log.v("IOMan.inContList", "> Получен список. Длина=" + len + ", тип="
			+ contType + ", постоянство=" + add + ", кол-во=" + num);

	switch (contType) {
		case Cont.ONE:
			if (num > 0) {
				ContOne[] ones = new ContOne[num];
				for (int q = 0; q < num; ++q) {
					int infoLen = io.readInt();
					ones[q] = new ContOne(io.readBytes(infoLen));
				}

				// Сообщить модулям
				if (add > 0) { // добавляются в список
					if (ones.length == 1) {
						Man.contMan.addCont(Cont.ONE, ones[0], Cont.M_IO);
					} else {
						Man.contMan.setList(Cont.ONE, ones);
					}
					Man.scrMan.showWin(null);
				} else { // НЕ добавляется в список
					Man.scrMan.inFind(Cont.ONE, ones);
				}

			}
			break;

		case Cont.ROOM:
			if (num > 0) {
				ContRoom[] rooms = new ContRoom[num];
				for (int q = 0; q < num; ++q) {
					int infoLen = io.readInt();
					rooms[q] = new ContRoom(io.readBytes(infoLen));
				}

				// Сообщить модулям
				if (add > 0) {
					if (rooms.length == 1) {
						// TODO Настроить contMan
						Man.contMan.addCont(Cont.ROOM, rooms[0], Cont.M_IO);
					} else {
						Man.contMan.setList(Cont.ROOM, rooms);
					}
					Man.scrMan.showWin(null);
				} else { // НЕ добавляется в список
					Man.scrMan.inFind(Cont.ROOM, rooms);
				}


			}
			break;

		case Cont.IN_ROOM:

			if (num > 0) {
				ContOne[] ones = new ContOne[num];
				for (int q = 0; q < num; ++q) {
					int infoLen = io.readInt();
					ones[q] = new ContOne(io.readBytes(infoLen));
				}

				Man.scrMan.inRoomWho(add, ones);
			}
			break;
		default:
			Log.e("IOMan.inContList", "Неизвестный тип контакта - " + contType);
	}

}


//// ================================================== Прочитать список контактов
//private void inContList() throws IOException {
//
//	int len = io.readInt(); // Общая длина
//	io.startRead();
//	int contType = io.readInt(); // Тип списка контактов
//	int add = io.readInt(); // Постоянство или номер комнаты
//	int num = io.readInt(); // Кол-во полученных результатов
//
//	Log.v("IOMan.inContList", "> Получен список. Длина=" + len + ", тип="
//			+ contType + ", постоянство=" + add + ", кол-во=" + num);
//
//	switch (contType) {
//		case Cont.ONE:
//			if (num > 0) {
//				ContOne[] ones = new ContOne[num];
//				for (int q = 0; q < num; ++q) {
//					int infoLen = io.readInt();
//					ones[q] = new ContOne(io.readBytes(infoLen));
//				}
//
//				// Сообщить модулям
//				if (add > 0) { // добавляются в список
//					if (ones.length == 1) {
//						Man.contMan.addCont(Cont.ONE, ones[0], Cont.M_IO);
//					} else {
//						Man.contMan.setList(Cont.ONE, ones);
//					}
//					Man.scrMan.showMain();
//				} else { // НЕ добавляется в список
//					Man.scrMan.inFind(Cont.ONE, ones);
//				}
//
//			}
//			break;
//
//		case Cont.ROOM:
//			if (num > 0) {
//				ContRoom[] rooms = new ContRoom[num];
//				for (int q = 0; q < num; ++q) {
//					int infoLen = io.readInt();
//					rooms[q] = new ContRoom(io.readBytes(infoLen));
//				}
//
//				// Сообщить модулям
//				if (add > 0) {
//					if (rooms.length == 1) {
//						// TODO Настроить contMan
//						Man.contMan.addCont(Cont.ROOM, rooms[0], Cont.M_IO);
//					} else {
//						Man.contMan.setList(Cont.ROOM, rooms);
//					}
//					Man.scrMan.showMain();
//				} else { // НЕ добавляется в список
//					Man.scrMan.inFind(Cont.ROOM, rooms);
//				}
//
//
//			}
//			break;
//
//		case Cont.IN_ROOM:
//
//			if (num > 0) {
//				ContOne[] ones = new ContOne[num];
//				for (int q = 0; q < num; ++q) {
//					int infoLen = io.readInt();
//					ones[q] = new ContOne(io.readBytes(infoLen));
//				}
//
//				Man.scrMan.inRoomWho(add, ones);
//			}
//			break;
//		default:
//			Log.e("IOMan.inContList", "Неизвестный тип контакта - " + contType);
//	}
//
//}
//
//
//// ============================================================= Удалить контакт
//public void inDelCont() throws IOException {
//
//	int contType = io.readInt();
//	int contNum = io.readInt();
//
//	Log.v("IOMan.inDelCont", "Удалить контакт, тип=" + contType + ", №"
//			+ contNum);
//	String name = Man.contMan.delCont(contType, contNum);
//	Man.scrMan.showAlert("Удаление контакта", name + " удалил свой приват.", 5,
//			false);
//}
//
//
//// ============================================================== На связи время
//private int inKeepAliveTime() {
//
//	Log.v("IOMan.inKeepAliveTime", ">");
//	setStatus("В сети");
//	return 0;
//}
//
//
//// ====================================================== Подтверждение доставки
//private void inResOne() throws IOException {
//
//	int contNum = io.readInt();
//	int messNum = io.readInt();
//	Log.v("IOMan.inResOne", "> Доставлено контакту №" + contNum + " сообщение №"
//			+ messNum);
//
//	// Сообщить модулям
//	Man.contMan.inResOne(contNum, messNum);
//	Man.scrMan.inRes(Cont.ONE, contNum);
//}
//
//
////============================================= Подтверждение доставки в комнату
//private void inResRoom() throws IOException {
//
//	int roomNum = io.readInt();
//	int messNum = io.readInt();
//	Log.v("IOMan.inResRoom", "> Доставлено в комнату N=" + roomNum
//			+ " сообщение №" + messNum);
//
//	// Сообщить модулям
//	Man.contMan.inResRoom(roomNum, messNum);
//	Man.scrMan.inRes(Cont.ROOM, roomNum);
//}
//
//
//// ================================================================== От сервера
//private void inServ() throws IOException {
//
//	String text;
//	int len = io.readInt();
//	io.startRead();
//	int sec = io.readInt();
//
//	text = Mat.iso2utf(io.readBytes(new byte[len - io.getReadLen()]));
//	Log.v("IOMan.inserv", "> Сервер (" + len + " байт), " + sec + "сек.: "
//			+ text);
//
//	// scrMan.vibra(Setting.vibraMessTime);
//	Man.scrMan.showAlert("Сервер:", text, sec, true);
//}
//
//
//// ========================================================== Получены настройки
//private void inSetting() throws IOException {
//
//	int len = io.readInt();
//	Log.v("IOMan.inSetting", "Получены настройки. Длина=" + len);
//
//	if (len > 0) {
//		Setting.fromBytes(io.readBytes(len));
//	}
//}
//
//
//// ============================================================= Статус контакта
//private void inStatOne() throws IOException {
//
//	StatOne statOne = new StatOne();
//
//	statOne.unserialize(io.readBytes(new byte[io.readInt()]));
//
//	Log.v("IOMan.inStatOne", "Получен статус: " + statOne);
//
//	Man.soundMan.play(MediaType.S_CONNECT);
//	Man.contMan.setStat(statOne);
//	// scrMan.refresh(Cont.ONE, statOne.fromNum);
//}
//
//
////=============================================================== Статус комнаты
//private void inStatRoom() throws IOException {
//
//	int len = io.readInt(); // Длина
//	io.startRead();
//
//	int roomNum = io.readInt(); // Номер комнаты
//
//	len -= io.getReadLen();
//	byte[] buf = io.readBytes(len); // Состояние комнаты
//
//
//	Log.v("IOMan.inStatRoom", "Получен статус комнаты №" + roomNum + " длина="
//			+ len);
//
//	this.setStatus(Man.contMan.inStatRoom(roomNum, buf));
//}
//
//

//================================================== Текстовое личное сообщение
private void inTextOne() {

	byte[] buf = new byte[BLK_SIZE]; // Буфер текста сообщения
	int fromNum = 0; // Номер контакта
	int messLen; // Длина сообщения
	int messNum = -1; // Номер сообщения

	MessOne mess = new MessOne(); // Сообщение

	mess.color = ContCol.T_IN_ONE; // Тип сообщения

	try {
		messLen = io.readInt();
		io.startRead();

		mess.h = io.readUnsByte(); // Читаем часы и флаги
		mess.m = io.readUnsByte(); // Читаем минуты

		int histState = (mess.h & 0xc0) >> 6; // Состояние истории

		if (histState == 0) { // Не история
			this.timeSleep = P_WORK;
		} else { // История
			this.timeSleep = P_HIST;
			mess.day = io.readInt();
			mess.mon = io.readInt();
			mess.year = io.readInt();
		}

		switch (histState) {
			case 0:
				// Не история, обычное
				break;
			case 1:
				mess.color = ContCol.T_IN_ONE;
				break;
			case 2:
				mess.color = ContCol.T_OUT_SERV;
				break;
			case 3:
				mess.color = ContCol.T_OUT_ADR;
				break;
			default:
				Log.e("IOMan.inTextOne", "Состояние истории: " + histState);
		}
		if ((mess.h & Mat.BIT5) != 0) {
			messNum = io.readInt(); // Читаем номер сообщения
		}
		mess.h &= 0x1f;
		fromNum = io.readInt(); // Читаем номер контакта

		messLen -= io.getReadLen(); // Вычисляем оставшуюся длину

		// Читаем текст
		while (messLen > BLK_SIZE) {
			io.readBytes(buf, 0, BLK_SIZE);
			mess.text += Mat.iso2utf(buf, BLK_SIZE);
			messLen -= BLK_SIZE;
		}
		io.readBytes(buf, 0, messLen);
		if (Setting.fRes && (messNum >= 0)) {
			outResOne(fromNum, messNum);
		}
		mess.text += Mat.iso2utf(buf, messLen);
		Log.v("IOMan.inTextOne", "> от N=" + fromNum + " messNum=" + messNum + " "
				+ messLen + " байт, " + mess);

		// Коррекция часов
		mess.h += Setting.timeSerShift;
		if (mess.h >= 24) {
			mess.h -= 24;
		}


		// Сообщить остальным модулям
		if (mess.year <= 0) { // Не история
			Man.soundMan.play(MediaType.S_MESS_ONE);
			Man.scrMan.vibrate(Setting.vibraMessTime);
		}

		Man.contMan.addTextOne(fromNum, mess);
		Man.scrMan.inText(Cont.ONE, fromNum, mess);

	} catch (Exception e) {
		Log.e("IOMan.inTextOne", "Не могу прочитать сообщение", e);
	}
}


//// ================================================== Текстовое личное сообщение
//private void inTextOne() {
//
//	byte[] buf = new byte[BLK_SIZE]; // Буфер текста сообщения
//	int fromNum = 0; // Номер контакта
//	int messLen; // Длина сообщения
//	int messNum = -1; // Номер сообщения
//
//	MessOne mess = new MessOne(); // Сообщение
//
//	mess.color = ContCol.T_IN_ONE; // Тип сообщения
//
//	try {
//		messLen = io.readInt();
//		io.startRead();
//
//		mess.h = io.readUnsByte(); // Читаем часы и флаги
//		mess.m = io.readUnsByte(); // Читаем минуты
//
//		int histState = (mess.h & 0xc0) >> 6; // Состояние истории
//
//		if (histState == 0) { // Не история
//			this.timeSleep = P_WORK;
//		} else { // История
//			this.timeSleep = P_HIST;
//			mess.day = io.readInt();
//			mess.mon = io.readInt();
//			mess.year = io.readInt();
//		}
//
//		switch (histState) {
//			case 0:
//				// Не история, обычное
//				break;
//			case 1:
//				mess.color = ContCol.T_IN_ONE;
//				break;
//			case 2:
//				mess.color = ContCol.T_OUT_SERV;
//				break;
//			case 3:
//				mess.color = ContCol.T_OUT_ADR;
//				break;
//			default:
//				Log.e("IOMan.inTextOne", "Состояние истории: " + histState);
//		}
//		if ((mess.h & Mat.BIT5) != 0) {
//			messNum = io.readInt(); // Читаем номер сообщения
//		}
//		mess.h &= 0x1f;
//		fromNum = io.readInt(); // Читаем номер контакта
//
//		messLen -= io.getReadLen(); // Вычисляем оставшуюся длину
//
//		// Читаем текст
//		while (messLen > BLK_SIZE) {
//			io.readBytes(buf, 0, BLK_SIZE);
//			mess.text += Mat.iso2utf(buf, BLK_SIZE);
//			messLen -= BLK_SIZE;
//		}
//		io.readBytes(buf, 0, messLen);
//		if (Setting.fRes && (messNum >= 0)) {
//			outResOne(fromNum, messNum);
//		}
//		mess.text += Mat.iso2utf(buf, messLen);
//		//		Log.v("IOMan.inTextOne", "> от N=" + fromNum + " messNum=" + messNum + " "
//		//				+ messLen + " байт, " + net.molchat.android.mess);
//
//		// Коррекция часов
//		mess.h += Setting.timeSerShift;
//		if (mess.h >= 24) {
//			mess.h -= 24;
//		}
//
//
//		// Сообщить остальным модулям
//		if (mess.year <= 0) { // Не история
//			Man.soundMan.play(MediaType.S_MESS_ONE);
//			Man.scrMan.vibrate(Setting.vibraMessTime);
//		}
//
//		Man.contMan.addTextOne(fromNum, mess);
//		Man.scrMan.inText(Cont.ONE, fromNum, mess);
//
//	} catch (Exception e) {
//		Log.e("IOMan.inTextOne", "Не могу прочитать сообщение", e);
//	}
//}
//
//

//========================================================= Текстовое в комнату
private void inTextRoom() {

	int f = 0; // Флаги
	int messLen; // Длина сообщения
	int messNum = -1; // Номер сообщения
	int roomNum = -1; // Номер комнаты

	MessRoom messRoom = new MessRoom(); // Сообщение
	messRoom.color = ContCol.T_IN_ROOM;

	byte[] buf = new byte[BLK_SIZE]; // Буфер текста сообщения

	try {
		messLen = io.readInt();
		io.startRead();

		f = io.readUnsByte();

		messRoom.h = io.readUnsByte(); // Читаем часы
		messRoom.m = io.readUnsByte(); // Читаем минуты

		if ((f & Mat.BIT6) == 0) {
			this.timeSleep = P_WORK;
		} else { // Читаем дату
			this.timeSleep = P_HIST;
			messRoom.day = io.readInt();
			messRoom.mon = io.readInt();
			messRoom.year = io.readInt();
		}

		roomNum = io.readInt(); // Номер комнаты

		messRoom.fromType = (f & (Mat.BIT3 | Mat.BIT4)) >> 3;
		switch (messRoom.fromType) { // Тип отправившего контакта

			case MessRoom.FROM_SERVER:
				messRoom.color = ContCol.T_IN_SERV;
				break;

			case MessRoom.FROM_ME:
				messRoom.color = ContCol.T_OUT_ADR;
				break;

			case MessRoom.FROM_ONE:
			case MessRoom.FROM_CACHE_OLD:
				// Только номер
				messRoom.fromNum = io.readInt();
				break;

			case MessRoom.FROM_CACHE_NEW:
				// Номер и ник
				messRoom.fromNum = io.readInt();
				messRoom.fromCache = io.readString();
				Man.contMan.addCache(messRoom.fromNum, messRoom.fromCache);
				messRoom.fromType = MessRoom.FROM_CACHE_OLD;
				break;

			default:
				Log.e("IOMan.inTextRoom", "Неизвестный тип отправителя: "
						+ messRoom.fromType);
		}

		messRoom.tarType = f & 0x07;
		switch (messRoom.tarType) { // Тип получающего контакта

			case MessRoom.TAR_ME:
				messRoom.fPrv = true;
				messRoom.color = ContCol.T_IN_PRIVATE;
				break;

			case MessRoom.TAR_ME_ALL:
			case MessRoom.TAR_ALL:
				break;

			case MessRoom.TAR_ONE:
			case MessRoom.TAR_CACHE_OLD:
				messRoom.tarNum = io.readInt();
				break;

			case MessRoom.TAR_CACHE_NEW:
				messRoom.tarNum = io.readInt();
				messRoom.tarCache = io.readString();
				Man.contMan.addCache(messRoom.tarNum, messRoom.tarCache);
				messRoom.tarType = MessRoom.TAR_CACHE_OLD;
				break;

			default:
				Log.e("PhMan.outTextRoom", "Неизвестный тип получателя: "
						+ messRoom.tarType);
		}

		if ((f & Mat.BIT5) != 0) {
			messNum = io.readInt();
		}

		messLen -= io.getReadLen(); // Вычисляем оставшуюся длину

		// Читаем текст
		while (messLen > BLK_SIZE) {
			io.readBytes(buf, 0, BLK_SIZE);
			messRoom.text += Mat.iso2utf(buf, BLK_SIZE);
			messLen -= BLK_SIZE;
		}
		io.readBytes(buf, 0, messLen);
		messRoom.text += Mat.iso2utf(buf, messLen);

		// Чтение сообщения завершено

		if (Setting.fRes && (messNum >= 0)) {
			//			outResOne(fromNum, messNum); TODO Отправить подтверждение доставки при необходимости
		}

		// Коррекция часов
		messRoom.h += Setting.timeSerShift;
		if (messRoom.h >= 24) {
			messRoom.h -= 24;
		}

		Log.v("IOMan.inTextRoom", "> " + messRoom);

		// Передать внешним модулям

		if (messRoom.year <= 0) { // Не история
			Man.soundMan.play(MediaType.S_MESS_ROOM);
			Man.scrMan.vibrate(Setting.vibraMessTime);
		}

		Man.contMan.addTextRoom(roomNum, messRoom);
		Man.scrMan.inText(Cont.ROOM, roomNum, messRoom);

	} catch (Exception e) {
		Log.e("IOMan.inTextRoom", "Не могу прочитать сообщение", e);
	}
}


//// ========================================================= Текстовое в комнату
//private void inTextRoom() {
//
//	int f = 0; // Флаги
//	int messLen; // Длина сообщения
//	int messNum = -1; // Номер сообщения
//	int roomNum = -1; // Номер комнаты
//
//	MessRoom messRoom = new MessRoom(); // Сообщение
//	messRoom.color = ContCol.T_IN_ROOM;
//
//	byte[] buf = new byte[BLK_SIZE]; // Буфер текста сообщения
//
//	try {
//		messLen = io.readInt();
//		io.startRead();
//
//		f = io.readUnsByte();
//
//		messRoom.h = io.readUnsByte(); // Читаем часы
//		messRoom.m = io.readUnsByte(); // Читаем минуты
//
//		if ((f & Mat.BIT6) == 0) {
//			this.timeSleep = P_WORK;
//		} else { // Читаем дату
//			this.timeSleep = P_HIST;
//			messRoom.day = io.readInt();
//			messRoom.mon = io.readInt();
//			messRoom.year = io.readInt();
//		}
//
//		roomNum = io.readInt(); // Номер комнаты
//
//		messRoom.fromType = (f & (Mat.BIT3 | Mat.BIT4)) >> 3;
//		switch (messRoom.fromType) { // Тип отправившего контакта
//
//			case MessRoom.FROM_SERVER:
//				messRoom.color = ContCol.T_IN_SERV;
//				break;
//
//			case MessRoom.FROM_ME:
//				messRoom.color = ContCol.T_OUT_ADR;
//				break;
//
//			case MessRoom.FROM_ONE:
//			case MessRoom.FROM_CACHE_OLD:
//				// Только номер
//				messRoom.fromNum = io.readInt();
//				break;
//
//			case MessRoom.FROM_CACHE_NEW:
//				// Номер и ник
//				messRoom.fromNum = io.readInt();
//				messRoom.fromCache = io.readString();
//				Man.contMan.addCache(messRoom.fromNum, messRoom.fromCache);
//				messRoom.fromType = MessRoom.FROM_CACHE_OLD;
//				break;
//
//			default:
//				Log.e("IOMan.inTextRoom", "Неизвестный тип отправителя: "
//						+ messRoom.fromType);
//		}
//
//		messRoom.tarType = f & 0x07;
//		switch (messRoom.tarType) { // Тип получающего контакта
//
//			case MessRoom.TAR_ME:
//				messRoom.fPrv = true;
//				messRoom.color = ContCol.T_IN_PRIVATE;
//				break;
//
//			case MessRoom.TAR_ME_ALL:
//			case MessRoom.TAR_ALL:
//				break;
//
//			case MessRoom.TAR_ONE:
//			case MessRoom.TAR_CACHE_OLD:
//				messRoom.tarNum = io.readInt();
//				break;
//
//			case MessRoom.TAR_CACHE_NEW:
//				messRoom.tarNum = io.readInt();
//				messRoom.tarCache = io.readString();
//				Man.contMan.addCache(messRoom.tarNum, messRoom.tarCache);
//				messRoom.tarType = MessRoom.TAR_CACHE_OLD;
//				break;
//
//			default:
//				Log.e("PhMan.outTextRoom", "Неизвестный тип получателя: "
//						+ messRoom.tarType);
//		}
//
//		if ((f & Mat.BIT5) != 0) {
//			messNum = io.readInt();
//		}
//
//		messLen -= io.getReadLen(); // Вычисляем оставшуюся длину
//
//		// Читаем текст
//		while (messLen > BLK_SIZE) {
//			io.readBytes(buf, 0, BLK_SIZE);
//			messRoom.text += Mat.iso2utf(buf, BLK_SIZE);
//			messLen -= BLK_SIZE;
//		}
//		io.readBytes(buf, 0, messLen);
//		messRoom.text += Mat.iso2utf(buf, messLen);
//
//		// Чтение сообщения завершено
//
//		if (Setting.fRes && (messNum >= 0)) {
//			//			outResOne(fromNum, messNum); TODO Отправить подтверждение доставки при необходимости
//		}
//
//		// Коррекция часов
//		messRoom.h += Setting.timeSerShift;
//		if (messRoom.h >= 24) {
//			messRoom.h -= 24;
//		}
//
//		//		Log.v("IOMan.inTextRoom", "> " + messRoom);
//
//		// Передать внешним модулям
//
//		if (messRoom.year <= 0) { // Не история
//			Man.soundMan.play(MediaType.S_MESS_ROOM);
//			Man.scrMan.vibrate(Setting.vibraMessTime);
//		}
//
//		Man.contMan.addTextRoom(roomNum, messRoom);
//		Man.scrMan.inText(Cont.ROOM, roomNum, messRoom);
//
//	} catch (Exception e) {
//		Log.e("IOMan.inTextRoom", "Не могу прочитать сообщение", e);
//	}
//}
//
//
//// ================================================== Добавить найденный контакт
//public void outAddCont(int contType, int contNum) throws IOException {
//
//	Log.v("IOMan.outAddCont", "< Тип " + contType + ", номер " + contNum);
//	io.start(ComType.vAddCont);
//	io.sendInt(contType);
//	io.sendInt(contNum);
//	//io.sendInt(slotNum); // Устарело с 14.02.2009
//	io.flush();
//	kaTick.update();
//}
//
//
//// ============================================================= Удалить контакт
//public void outDelCont(int contType, int contNum) throws IOException {
//
//	io.start(ComType.vDelCont);
//	io.sendInt(contType);
//	io.sendInt(contNum);
//	io.flush();
//	kaTick.update();
//}
//
//
//// ================================================= Найти контакт с параметрами
//public void outFindOne(String name, int maxResults) throws IOException {
//
//	Log.v("IOMan.outFindOne", "< Запрос поиска контакта " + name);
//	io.start(ComType.vFindOne);
//	io.sendLen();
//	io.sendInt(maxResults);
//	io.sendInt(ContOne.cLogin);
//	io.sendInt(name.length());
//	io.sendBytes(Mat.utf2iso(name));
//	// Сюда добавить остальные критерии поиска
//	io.flush();
//	kaTick.update();
//}
//
//
//// ================================================= Найти комнату с параметрами
//public void outFindRoom(String name, int maxResults) throws IOException {
//
//	Log.v("IOMan.outFindRoom", "< Запрос поиска комнаты " + name);
//	io.start(ComType.vFindRoom);
//	io.sendLen();
//	io.sendInt(maxResults);
//	io.sendInt(ContRoom.NAME);
//	io.sendInt(name.length());
//	io.sendBytes(Mat.utf2iso(name));
//	// Сюда добавить остальные критерии поиска
//	io.flush();
//	kaTick.update();
//}
//
//
//// =============================================================== Отправить CRC
///*
// * public void outCRC(int contType, int CRC) { Log.v("IOMan.outCRC",
// * "< Отправка CRC. Тип=" + contType + ", CRC=" + CRC); io.start(ComType.vCRC);
// * io.sendInt(contType); io.sendInt(CRC); io.flush(); kaTick.update(); }/
// */


//================================================================= outKeepAlive
/**
 * Send keep alive message.
 * 
 * @throws IOException
 */
public void outKeepAlive() throws IOException {

	Log.v("IOMan.outKeepAlive", "< KeepAlive");
	io.start(ComType.mKeepAlive);
	io.flush();
	kaTick.update();
}


////===================================================== Передача идентификаторов
//private void outLogin() {
//
//	Log.v("IOMan.outLogin", "< Передача идентификаторов");
//
//	try {
//		InputStream is = this.getClass().getResourceAsStream("/res/empty.gif");
//		byte[] b = new byte[16];
//		is.read(b);
//		is.close();
//
//		io.start(Setting.TYPE);
//		// Версия
//		io.sendBytes(new byte[] {
//				(byte) ((Integer.parseInt(Setting.VER.substring(0, 2))) & 0xff),
//				(byte) ((Integer.parseInt(Setting.VER.substring(3, 5))) & 0xff),
//				(byte) ((Integer.parseInt(Setting.VER.substring(6, 10)) - 2000) & 0xff) });
//
//
//		// Идентификатор
//		io.sendInt(ComType.mID);
//		io.sendBytes(b);
//
//		// Логин и пароль
//		io.sendInt(ComType.vLogin);
//		io.sendString(Setting.login);
//		io.sendString(Setting.password);
//
//		// Контрольные суммы CRC
//		int crc = -1;
//
//		for (int q = Cont.ROOM; q <= Cont.ONE; ++q) {
//			crc = Man.contMan.getCRC(q);
//			io.sendInt(ComType.vCRC);
//			io.sendInt(q);
//			io.sendInt(crc);
//		}
//
//		io.flush();
//
//		kaTick.update();
//
//		setOnline(true);
//		resumeThr();
//
//	} catch (Exception e) {
//		Log.e("IOMan.outLogin", "Ошибка передачи идентификаторов", e);
//	}
//
//}


// ====================================================== Подтверждение доставки
private void outResOne(int contNum, int messNum) throws IOException {

	io.start(ComType.vResOne);
	io.sendInt(contNum);
	io.sendInt(messNum);
	io.flush();
	kaTick.update();
}


////======================================= Запрос списка присутствующих в комнате
//public void outRoomWho(int roomNum) throws IOException {
//
//	Log.v("IOMan.outRoomWho", "< Запрос списка контактов комнаты " + roomNum);
//	io.start(ComType.vRoomWho);
//	io.sendInt(roomNum);
//	io.flush();
//	kaTick.update();
//}
//
//
////===================================================== Передача идентификаторов
//private void outSetting(byte[] bytes) throws IOException {
//
//	Log.v("IOMan.run", "> Настройки");
//	io.start(ComType.vSetting);
//	io.sendLen();
//	io.sendBytes(bytes);
//	io.flush();
//	kaTick.update();
//}
//
//
//// ============================================================ Текстовое личное
//public void outTextOne(MessOne messOne, int tarOneNum, int messNum)
//		throws IOException {
//
//	Log.v("IOMan.outTextOne", "< " + messOne);
//	io.start(ComType.vTextOne);
//	io.sendLen();
//	if (Setting.fRes) {
//		io.sendInt(messNum + 1);
//	}
//	io.sendInt(tarOneNum);
//	io.sendBytes(Mat.utf2iso(messOne.text));
//	io.flush(); /**/
//	kaTick.update();
//}
//
//
//// =========================================================== Текстовое комната
//public void outTextRoom(int tarRoomNum, int messNum, MessRoom mess)
//		throws IOException {
//
//	Log.v("IOMan.outTextRoom", "< В комн=" + tarRoomNum + ", messNum=" + messNum
//			+ ", сообщение: " + mess);
//	int f = 0;
//
//	f |= mess.tarType & 0x03;
//
//	if (mess.fPrv) {
//		f |= Mat.BIT3;
//	}
//
//	if ((messNum >= 0) && (Setting.fRes)) {
//		f |= Mat.BIT2;
//	}
//
//	io.start(ComType.vTextRoom);
//	io.sendLen();
//	io.sendByte((byte) f);
//	io.sendInt(tarRoomNum);
//
//	switch (mess.tarType) {
//		case MessRoom.TAR_CACHE_OLD:
//		case MessRoom.TAR_ONE:
//		case MessRoom.TAR_IN_ROOM:
//			io.sendInt(mess.tarNum);
//	}
//
//	if ((f & Mat.BIT2) != 0) {
//		io.sendInt(messNum);
//	}
//	io.sendBytes(Mat.utf2iso(mess.text));
//	io.flush();
//	kaTick.update();
//}


}
